
export interface OnDestory<T = any> {
    onDestory(): Promise<T>;
}

export interface OnInit<T = any> {
    onInit(): Promise<T>;
}

const lifeCycles: Set<any> = new Set()

export const onDestory = async () => {
    const list = [...lifeCycles]
    for (let app of list) {
        if (app.onDestory) await app.onDestory().catch(noop)
    }
    lifeCycles.clear()
    process.nextTick(() => {
        process.exit(0);
    });
}

export const getLifecycle = <T>(filter: (val: any) => boolean): T | undefined => {
    const list = [...lifeCycles]
    return list.find(filter)
}
export function registerLifeCycle<T = any>(app: T) {
    lifeCycles.add(app)
}
export function registerLifeCycles<T = any>(apps: T[]) {
    apps.map(app => lifeCycles.add(app))
}
export const onInit = async () => {
    const list = [...lifeCycles]
    for (let app of list) {
        if (app.onInit) await app.onInit().catch(noop)
    }
}

export function noop(...args: any[]) {
    console.log(args)
}

export function registeOnDestoryHook() {
    // Ctrl+C
    process.on('SIGINT', onDestory);
    // kill命令
    process.on('SIGTERM', onDestory);
    // abort命令
    process.on('SIGABRT', onDestory);
    // 其他信号
    process.on('uncaughtException', (err) => {
        console.error('Uncaught Exception:', err);
        onDestory().catch(noop)
    });
    process.on('unhandledRejection', (reason, promise) => {
        console.error(`Unhandled Rejection promise: ${promise} reason:`, reason);
        onDestory().catch(noop)
    });
}

export interface Application {
    listen(): Promise<Function>;
}

export enum MessageType {
    UNKNOWN = 0,
    REQUEST = 1,
    RESPONSE = 2,
    ERROR = 3,
    SENDTO = 4,
    SENDTOROOM = 5,
}

export interface Message<T = any> {
    type: MessageType;
    payload: T;
}
export function isMessage(val: any): val is Message {
    if (!val) return false;
    return isSendToMessage(val) || isSendToRoomMessage(val) || isErrorMessage(val) || isRequestMessage(val) || isResponseMessage(val)
}
export function isSendToMessage(val: Message<any>): val is Message<SendToPayload> {
    return val.type === MessageType.SENDTO;
}
export function isSendToRoomMessage(val: Message<any>): val is Message<SendToRoomPayload> {
    return val.type === MessageType.SENDTOROOM;
}
export type MessageBuffer = Message<Buffer>;
export type RequestMessage = Message<RequestPayload>;
export function isRequestMessage(val: Message<any>): val is RequestMessage {
    return val.type === MessageType.REQUEST;
}
export interface RequestPayload {
    name: string;
    appid: string;
    reqid: string;
    method: string;
    path: string;
    data: Buffer;
    headers: Buffer;
    ip: string;
    version: string;
}

export type ResponseMessage = Message<ResponsePayload>;
export function isErrorMessage(val: Message<any>): val is ErrorMessage {
    return val.type === MessageType.ERROR;
}
export interface ResponsePayload {
    appid: string;
    reqid: string;
    data: Buffer;
    headers: Buffer;
    from: string;
}


export type ErrorMessage = Message<ErrorPayload>;
export function isResponseMessage(val: Message<any>): val is ResponseMessage {
    return val.type === MessageType.RESPONSE;
}
export interface ErrorPayload {
    appid: string;
    reqid: string;
    message: string;
    stack: string;
    from: string;
    data: Buffer;
}

export function isErrorPayload(val: any): val is ErrorPayload {
    return !!val?.message
}


export interface SendToPayload {
    from: string;
    to: string;
    data: Buffer;
}

export function isSendToPayload(val: any): val is SendToPayload {
    return !!val.to
}

export interface SendToRoomPayload {
    from: string;
    roomid: string;
    data: Buffer;
}

export function isSendToRoomPayload(val: any): val is SendToRoomPayload {
    return !!val.roomid
}
